package com.bjy.qa.util.fileparser;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.bjy.qa.entity.functionaltest.Api;
import com.bjy.qa.entity.functionaltest.ApiParam;
import com.bjy.qa.entity.functionaltest.CurlDTO;
import com.bjy.qa.entity.functionaltest.CurlImportDTO;
import com.bjy.qa.enumtype.CatalogType;
import com.bjy.qa.enumtype.ParamKind;
import com.bjy.qa.exception.MyException;
import com.bjy.qa.util.UrlUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.saasquatch.jsonschemainferrer.DefaultPolicies;
import com.saasquatch.jsonschemainferrer.JsonSchemaInferrer;
import com.saasquatch.jsonschemainferrer.SpecVersion;
import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 *  cURL 格式文件解析器，将 cURL 格式解析为 Api 对象
 */
public class CurlParser {
    private static final ObjectMapper mapper = new ObjectMapper();
    private static final JsonSchemaInferrer inferrer = JsonSchemaInferrer.newBuilder()
            .setSpecVersion(SpecVersion.DRAFT_04)
//            .addFormatInferrers(FormatInferrers.email(), FormatInferrers.ip()) // 校验器（格式推断器：邮件地址、ip 地址）
//            .setAdditionalPropertiesPolicy(AdditionalPropertiesPolicies.notAllowed()) // 设置附加属性策略（AdditionalPropertiesPolicies） 。notAllowed：不允许附加属性
//            .setRequiredPolicy(RequiredPolicies.nonNullCommonFields()) // 设置必填项（Required） 。nonNullCommonFields：非空字段都会被设置为必填项
//            .addEnumExtractors(EnumExtractors.validEnum(java.time.Month.class), EnumExtractors.validEnum(java.time.DayOfWeek.class)) // 枚举提取器
            .setDefaultPolicy(DefaultPolicies.useFirstSamples())
            .build();

    /**
     * 将 cURL 格式解析为 Api 对象
     * @param curlImportDTO cURL Import DTO 模型
     * @return
     */
    public Api toApi(CurlImportDTO curlImportDTO) {
        Api api = parserBasePostman(curlImportDTO.getCurl()); // 生成 api

        parserGet(curlImportDTO, api); // 解析 get 请求
        parserPost(curlImportDTO, api); // 解析 post 请求
        parserPut(curlImportDTO, api); // 解析 put 请求
        parserDelete(curlImportDTO, api); // 解析 delete 请求
        parserPatch(curlImportDTO, api); // 解析 patch 请求

        return api;
    }

    /**
     * 解析 get 请求
     * @param curlImportDTO cURL Import DTO 模型
     * @param api api 对象
     */
    private void parserGet(CurlImportDTO curlImportDTO, Api api) {
        if ("GET".equalsIgnoreCase(curlImportDTO.getCurl().getMethod())) {
            api.setMethod("GET");

            api.getApiParams().add(parserHead(curlImportDTO.isIgnoreGenericHeader(), curlImportDTO.getCurl())); // 解析 head 参数
            api.getApiParams().add(parserParams(curlImportDTO.getCurl())); // 解析 query（url param） 参数
            api.getApiParams().add(parserBody(curlImportDTO.getCurl(), api)); // 解析 body 参数
            api.getApiParams().add(parserCookie(curlImportDTO.getCurl())); // 解析 cookie 参数
            api.getApiParams().add(parserResponses()); // 解析 responses(assert) 参数
        }
    }

    /**
     * 解析 post 请求
     * @param curlImportDTO cURL Import DTO 模型
     * @param api api 对象
     */
    private void parserPost(CurlImportDTO curlImportDTO, Api api) {
        if ("POST".equalsIgnoreCase(curlImportDTO.getCurl().getMethod())) {
            api.setMethod("POST");

            api.getApiParams().add(parserHead(curlImportDTO.isIgnoreGenericHeader(), curlImportDTO.getCurl())); // 解析 head 参数
            api.getApiParams().add(parserParams(curlImportDTO.getCurl())); // 解析 query（url param） 参数
            api.getApiParams().add(parserBody(curlImportDTO.getCurl(), api)); // 解析 body 参数
            api.getApiParams().add(parserCookie(curlImportDTO.getCurl())); // 解析 cookie 参数
            api.getApiParams().add(parserResponses()); // 解析 responses(assert) 参数
        }
    }

    /**
     * 解析 put 请求
     * @param curlImportDTO cURL Import DTO 模型
     * @param api api 对象
     */
    private void parserPut(CurlImportDTO curlImportDTO, Api api) {
        if ("PUT".equalsIgnoreCase(curlImportDTO.getCurl().getMethod())) {
            api.setMethod("PUT");

            api.getApiParams().add(parserHead(curlImportDTO.isIgnoreGenericHeader(), curlImportDTO.getCurl())); // 解析 head 参数
            api.getApiParams().add(parserParams(curlImportDTO.getCurl())); // 解析 query（url param） 参数
            api.getApiParams().add(parserBody(curlImportDTO.getCurl(), api)); // 解析 body 参数
            api.getApiParams().add(parserCookie(curlImportDTO.getCurl())); // 解析 cookie 参数
            api.getApiParams().add(parserResponses()); // 解析 responses(assert) 参数
        }
    }

    /**
     * 解析 delete 请求
     * @param curlImportDTO cURL Import DTO 模型
     * @param api api 对象
     */
    private void parserDelete(CurlImportDTO curlImportDTO, Api api) {
        if ("DELETE".equalsIgnoreCase(curlImportDTO.getCurl().getMethod())) {
            api.setMethod("DELETE");

            api.getApiParams().add(parserHead(curlImportDTO.isIgnoreGenericHeader(), curlImportDTO.getCurl())); // 解析 head 参数
            api.getApiParams().add(parserParams(curlImportDTO.getCurl())); // 解析 query（url param） 参数
            api.getApiParams().add(parserBody(curlImportDTO.getCurl(), api)); // 解析 body 参数
            api.getApiParams().add(parserCookie(curlImportDTO.getCurl())); // 解析 cookie 参数
            api.getApiParams().add(parserResponses()); // 解析 responses(assert) 参数
        }
    }

    /**
     * 解析 patch 请求
     * @param curlImportDTO cURL Import DTO 模型
     * @param api api 对象
     */
    private void parserPatch(CurlImportDTO curlImportDTO, Api api) {
        if ("PATCH".equalsIgnoreCase(curlImportDTO.getCurl().getMethod())) {
            api.setMethod("PATCH");

            api.getApiParams().add(parserHead(curlImportDTO.isIgnoreGenericHeader(), curlImportDTO.getCurl())); // 解析 head 参数
            api.getApiParams().add(parserParams(curlImportDTO.getCurl())); // 解析 query（url param） 参数
            api.getApiParams().add(parserBody(curlImportDTO.getCurl(), api)); // 解析 body 参数
            api.getApiParams().add(parserCookie(curlImportDTO.getCurl())); // 解析 cookie 参数
            api.getApiParams().add(parserResponses()); // 解析 responses(assert) 参数
        }
    }

    /**
     * 解析 api 的基础 信息（name、host、uri），并返回 api 对象
     * @param curlDTO cURL DTO 模型
     * @return
     */
    private Api parserBasePostman(CurlDTO curlDTO) {
        String host = UrlUtil.getHost(curlDTO.getUrl());
        String uri = UrlUtil.getUri(curlDTO.getUrl());

        Api api = new Api();
        api.setName(uri);
        api.setHost(host);
        api.setUri(uri);
        api.setType(CatalogType.INTERFACE_HTTP.getValue());
        api.setApiParams(new ArrayList<>());

        return api;
    }

    /**
     * 解析 header 参数
     * @param ignoreGenericHeader 是否忽略通用 header
     * @param curlDTO cURL DTO 模型
     * @return
     */
    private ApiParam parserHead(boolean ignoreGenericHeader, CurlDTO curlDTO) {
        com.alibaba.fastjson.JSONObject schema = JSON.parseObject("{\"type\":\"object\",\"properties\":{}}");
        Map<String, Map<String, String>> properties = new HashMap<>();

        for (Map.Entry<String, Object> head : curlDTO.getHeader().entrySet()) {
            String key = head.getKey().toLowerCase();
            if (ignoreGenericHeader && ( key.indexOf("accept") == 0
                    || key.indexOf("referer") == 0
                    || key.indexOf("sec") == 0
                    || key.indexOf("cache") == 0
                    || key.indexOf("connection") == 0
                    || key.indexOf("user-agent") == 0
                    || key.indexOf("origin") == 0)) { // 忽略通用 Header

            } else {
                if (key.indexOf("content-type") == 0 || key.indexOf("cookie") == 0 || key.indexOf("content-length") == 0) {

                } else {
                    Map<String, String> propertie = new HashMap<>();
                    propertie.put("type", "string");
                    propertie.put("default", head.getValue().toString());
                    properties.put(head.getKey(), propertie);
                }
            }
        }

        if (properties.size() != 0) {
            schema.getObject("properties", Map.class).putAll(properties);
        }

        ApiParam apiParam = new ApiParam();
        apiParam.setKind(ParamKind.KIND_HEADER.getValue());
        apiParam.setName(ParamKind.KIND_HEADER.getName());
        apiParam.setDes(JSON.toJSONString(schema, SerializerFeature.DisableCircularReferenceDetect));
        apiParam.setExample("{}");

        return apiParam;
    }

    /**
     * 解析 query（url param） 参数
     * @param curlDTO cURL DTO 模型
     * @return
     */
    private ApiParam parserParams(CurlDTO curlDTO) {
        com.alibaba.fastjson.JSONObject schema = JSON.parseObject("{\"type\":\"object\",\"properties\":{}}");
        Map<String, Map<String, String>> properties = new HashMap<>();

        UrlUtil.param2KeyValueStore(UrlUtil.getQuery(curlDTO.getUrl())).forEach(parameter -> {
            Map<String, String> propertie = new HashMap<>();
            propertie.put("type", "string");
            propertie.put("default", parameter.getValue().toString());
            properties.put(parameter.getName(), propertie);
        });

        if (properties.size() != 0) {
            schema.getObject("properties", Map.class).putAll(properties);
        }

        ApiParam apiParam = new ApiParam();
        apiParam.setKind(ParamKind.KIND_URL_PARAM.getValue());
        apiParam.setName(ParamKind.KIND_URL_PARAM.getName());
        apiParam.setDes(JSON.toJSONString(schema, SerializerFeature.DisableCircularReferenceDetect));
        apiParam.setExample("{}");

        return apiParam;
    }

    /**
     * 解析 body 参数
     * @param curlDTO cURL DTO 模型
     * @param api api 对象
     * @return
     */
    private ApiParam parserBody(CurlDTO curlDTO, Api api) {
        ApiParam apiParam = new ApiParam();
        apiParam.setKind(ParamKind.KIND_BODY.getValue());
        apiParam.setName(ParamKind.KIND_BODY.getName());
        apiParam.setDes("{\"type\":\"object\",\"properties\":{}}");
        apiParam.setExample("{}");

        api.setExtra1("FORM"); // 默认设置为 form 类型，后边遇到 json 类型会覆盖

        for (Map.Entry<String, Object> head : curlDTO.getHeader().entrySet()) {
            if (head.getKey().equalsIgnoreCase("content-type")) {
                String contentType = head.getValue().toString().toLowerCase();
                if (contentType.indexOf("application/json") == 0) {
                    api.setExtra1("JSON"); // 设置为 json 类型（覆盖）
                } else if (contentType.indexOf("application/x-www-form-urlencoded") == 0) {
                    api.setExtra1("FORM"); // 设置为 form 类型
                } else {
                    throw new MyException("cURL 导入错误，暂不支持的 Content-Type: " + contentType);
                }
            }
        }

        if (StringUtils.isNotBlank(curlDTO.getBody())) {
            if ("FORM".equals(api.getExtra1())) {
                com.alibaba.fastjson.JSONObject schema = JSON.parseObject("{\"type\":\"object\",\"properties\":{}}");

                Map<String, Map<String, String>> properties = new HashMap<>();
                UrlUtil.param2KeyValueStore(curlDTO.getBody()).forEach(parameter -> {
                    Map<String, String> propertie = new HashMap<>();
                    propertie.put("type", "string");
                    propertie.put("default", parameter.getValue().toString());
                    properties.put(parameter.getName(), propertie);
                });

                if (properties.size() != 0) {
                    schema.getObject("properties", Map.class).putAll(properties);
                }

                apiParam.setDes(com.alibaba.fastjson.JSONObject.toJSONString(schema));
            } else if ("JSON".equals(api.getExtra1())) {
                try {
                    ObjectNode inferForSample = inferrer.inferForSample(mapper.readTree(curlDTO.getBody()));
                    apiParam.setDes(inferForSample.toString());
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        return apiParam;
    }

    /**
     * 解析 cookie 参数
     * @param curlDTO cURL DTO 模型
     * @return
     */
    private ApiParam parserCookie(CurlDTO curlDTO) {
        com.alibaba.fastjson.JSONObject schema = JSON.parseObject("{\"type\":\"object\",\"properties\":{}}");
        Map<String, Map<String, String>> properties = new HashMap<>();

        for (Map.Entry<String, Object> head : curlDTO.getHeader().entrySet()) {
            if (head.getKey().equalsIgnoreCase("cookie")) {
                UrlUtil.cookie2KeyValueStore(head.getValue().toString()).forEach(cookie -> {
                    Map<String, String> propertie = new HashMap<>();
                    propertie.put("type", "string");
                    propertie.put("default", cookie.getValue().toString());
                    properties.put(cookie.getName(), propertie);
                });
            }
        }

        if (properties.size() != 0) {
            schema.getObject("properties", Map.class).putAll(properties);
        }

        ApiParam apiParam = new ApiParam();
        apiParam.setKind(ParamKind.KIND_COOKIE.getValue());
        apiParam.setName(ParamKind.KIND_COOKIE.getName());
        apiParam.setDes(JSON.toJSONString(schema, SerializerFeature.DisableCircularReferenceDetect));
        apiParam.setExample("{}");

        return apiParam;
    }

    /**
     * 解析 responses(assert) 参数
     * @return
     */
    private ApiParam parserResponses() {
        ApiParam apiParam = new ApiParam();
        apiParam.setKind(ParamKind.KIND_ASSERT.getValue());
        apiParam.setName(ParamKind.KIND_ASSERT.getName());
        apiParam.setDes("{\"type\":\"object\",\"properties\":{}}");
        apiParam.setExample("{}");
        return apiParam;
    }
}